package alphabeta;
import evaluator.ElectricalResistanceFinder;
import game.Cell;
import game.HexBoard;
import game.HexGameFrame;
import game.Location;

import java.util.ArrayList;

import players.Player;
import template.Library;
import template.NodeGroup;

public class ABSearch {

  public static int depthLimit = 2; 
  
  private final HexBoard originalBoard;
  private final Player originalPlayer;
  

  public ABSearch(HexBoard hexBoard, Player player) {
    originalBoard = hexBoard.clone();
    originalPlayer = player;
  } 
  
  ///computeUtility: Calculate the cost for the move
  private MoveEvaluation computeUtility(HexBoard hexBoard) {
    // Strategy may be offense, defense, combined.
    // offense  -> myResistance
    // defense  -> otherResistance
    // combined -> myResistance/otherResistance
    int whichPlayer = hexBoard.whichPlayer(originalPlayer);
    double myResistance    = strategyIsDefense(whichPlayer) ? -1 : estimate(hexBoard, originalPlayer);
    double otherResistance = 
      strategyIsOffense(whichPlayer) ? -1 : estimate(hexBoard, hexBoard.getOtherPlayer(originalPlayer));
    return new MoveEvaluation(myResistance, otherResistance, whichPlayer);
  }
  
  
  //estimate(): Check which player is winning so far
  private int estimate(HexBoard hexBoard, Player player) {
    HexBoard board = hexBoard.clone();
    
    ElectricalResistanceFinder e = new ElectricalResistanceFinder(board, player) ;

    // Apply Template Library
    Library library = new Library();
    NodeGroup group = library.getLists(board);
    ArrayList<Cell> pzero, pvcc;
    
    if(player.equals(hexBoard.getLeftRightPlayer()))
    {
      pvcc = group.getPlayer1Zero();  //left-edge
      pzero = group.getPlayer1Size(); //right-edge  
    }
    else
    {
      pvcc = group.getPlayer2Zero();  //top-edge
      pzero = group.getPlayer2Size(); //bottom-edge
    }   
    
    // Add Virtual Connections...
    //Player 1:   Left:VCC    0:Right
    //Player 2:   Top:VCC     0:Bottom
    //connectToLeftOrTop()     -- For VCC
    //connectToBottomOrRight() -- For 0   
    //a) 0 Voltage
    for(int i = 0; i < pzero.size(); i++)
    {
      Cell cell = pzero.get(i);
      //System.out.println( player.getName() + "; ZERO: (" + cell.getX() + "," + cell.getY() + ")");
      e.connectToBottomOrRight(cell);
    }   
    
    //b) VCC
    for(int i = 0; i < pvcc.size(); i++)
    {
      Cell cell = pvcc.get(i);
      //System.out.println( player.getName() + "; VCC: (" + cell.getX() + "," + cell.getY() + ")");
      e.connectToTopOrLeft(cell);
    }
    
    // Calculate Resistance 
    return (int)e.getEqResistance() ;   
  }
  
  /*  
  //estimate(): Check which player is winning so far
  private int estimate_(HexBoard hexBoard)
  {
    //1. Apply Template Library
    Library library = new Library();
    HexBoard hexBoard1 = library.fill(hexBoard.clone());
    //hexBoard1.Display();
  
    //2. Call H-Search
    HSearch hSearch = new HSearch();
    HexBoard hexBoard2 = hSearch.SearchConnections(hexBoard.clone());     
    //hexBoard2.Display();
    
    //3. Merge Result
    HexBoard hexBoard3 = Utility.MergeBoard(hexBoard1, hexBoard2);
    //hexBoard3.Display();      
    
    //4. Calculate Resistance 
    Player player = hexBoard.getLastMove().getPlayer();
    ElectricalResistanceFinder e = new ElectricalResistanceFinder(hexBoard3, player) ;
  
    return (int)e.getEqResistance() ;   
  } 
*/  
  
  
  ///getBestMove(): Select BestMove for the given player
  public Cell getBestMove() {
    int depth = 1;
    
    int whichPlayer = originalBoard.whichPlayer(originalPlayer);
    long start = System.currentTimeMillis();
    // if strategyIsDefense() maximize; otherwise minimize;
    MoveEvaluation bestMove = 
      getMiniMaxValue(!strategyIsDefense(whichPlayer), originalBoard, originalPlayer, depth, 0, Double.MAX_VALUE);
    long stop = System.currentTimeMillis();
    
    double score = bestMove.getScore();
    if (strategyIsDefense(whichPlayer) ? score == 0 : score == Double.MAX_VALUE ) 
      bestMove.setLocation(Location.RESIGN_MOVE);
    if (HexGameFrame.traceButtonGroup.getIndex()>0) {
      System.out.println(originalPlayer + " best move: " + bestMove.getLocation() + 
                         ", Score: " + bestMove.getScore() +
                         ". Execution Time: " + (stop-start)/1000f + " secs.");
    }
    return new Cell(bestMove.getLocation(), originalPlayer);
  }
  

  /**
   * Find min or max value
   * @param min true if looking for min; false if looking for max
   * @param hexBoard
   * @param player
   * @param depth
   * @param alpha
   * @param beta
   * @return
   */
  private MoveEvaluation getMiniMaxValue(boolean min, HexBoard hexBoard, 
                                        Player player, int depth, 
                                        double alpha, double beta) {

    Player winner = hexBoard.getWinner();
    int whichPlayer = hexBoard.whichPlayer(player);
    if (winner != null) {
      return winner.equals(player) 
                ? new MoveEvaluation(min?0:-1, min?-1:Double.MAX_VALUE, whichPlayer)
                : new MoveEvaluation(min?Double.MAX_VALUE:-1, min?-1:0, whichPlayer);
    }

    if (depth > depthLimit) return computeUtility(hexBoard);

    ArrayList<Location> childrenMoveList = Utility.getPossibleMoves(hexBoard);
    MoveEvaluation best = null; 
    for (Location move: childrenMoveList) {
      hexBoard.setMove(new Cell(move, player)); 
      
      MoveEvaluation moveEval = 
        getMiniMaxValue(!min, hexBoard, hexBoard.getOtherPlayer(player), depth+1, alpha, beta);      
      if ( best == null ) best = moveEval;
      moveEval.setLocation(move);

      double score = moveEval.getScore();
      if (min) {
        if (score < best.getScore()) {
          best = moveEval;
          beta = Math.min(beta, score);
        }
      } else {
        if (score > best.getScore()) {
          best = moveEval;
          alpha = Math.max(alpha, score);
        }
      }
      if (depth <= HexGameFrame.traceButtonGroup.getIndex())
        System.out.println(hexBoard.getLastNMovesString(depth) + ": " + moveEval.getScore() + 
                           " " + best); // + " <" + alpha + " " + beta + ">");
      hexBoard.undoMove();
      if (alpha >= beta) return best;
    }
    return best;
  }
  
  
  private boolean strategyIsDefense(int whichPlayer) {
    return HexGameFrame.strategyIsDefense(whichPlayer);
  }


  private boolean strategyIsOffense(int whichPlayer) {
    return HexGameFrame.strategyIsOffense(whichPlayer);
  } 
  

}